package com.pig.easy.bpm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.pig.easy.bpm.constant.BpmConstant;
import com.pig.easy.bpm.dto.request.NodeInfoReqDTO;
import com.pig.easy.bpm.dto.request.ProcessDetailReqDTO;
import com.pig.easy.bpm.dto.request.ProcessReqDTO;
import com.pig.easy.bpm.dto.response.*;
import com.pig.easy.bpm.entity.ProcessDO;
import com.pig.easy.bpm.entityError.EntityError;
import com.pig.easy.bpm.mapper.ProcessMapper;
import com.pig.easy.bpm.service.*;
import com.pig.easy.bpm.utils.BeanUtils;
import com.pig.easy.bpm.utils.BestBpmAsset;
import com.pig.easy.bpm.utils.CommonUtils;
import com.pig.easy.bpm.utils.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Lazy;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 流程表 服务实现类
 * </p>
 *
 * @author pig
 * @since 2020-05-20
 */
@org.apache.dubbo.config.annotation.Service
@Slf4j
public class ProcessServiceImpl extends BeseServiceImpl<ProcessMapper, ProcessDO> implements ProcessService {

    @Autowired
    ProcessMapper processMapper;

    @Autowired
    @Lazy
    ProcessDetailService processDetailService;
    @Autowired
    NodeService nodeService;
    @Autowired
    FormService formService;
    @Autowired
    DictItemService dictItemService;

    @Override
    public Result<PageInfo> getListByCondition(ProcessReqDTO processReqDTO) {

        if (processReqDTO == null) {
            return Result.responseError(EntityError.ILLEGAL_ARGUMENT_ERROR);
        }
        int pageIndex = CommonUtils.evalInt(processReqDTO.getPageIndex(), DEFAULT_PAGE_INDEX);
        int pageSize = CommonUtils.evalInt(processReqDTO.getPageSize(), DEFAULT_PAGE_SIZE);

        PageHelper.startPage(pageIndex, pageSize);
        ProcessDO process = BeanUtils.switchToDO(processReqDTO, ProcessDO.class);
        process.setValidState(VALID_STATE);
        List<ProcessInfoDTO> processDTOList = processMapper.getListByCondition(process);
        if (processDTOList == null) {
            processDTOList = new ArrayList<>();
        }
        PageInfo<ProcessInfoDTO> pageInfo = new PageInfo<>(processDTOList);
        return Result.responseSuccess(pageInfo);
    }

    @Cacheable(value = "processDetail", key = "#p0", unless = "#result == null || #result.entityError == null || #result.entityError.code != 200")
    @Override
    public Result<ProcessDTO> getProcessById(Long processId) {

        if (CommonUtils.evalLong(processId) < 0) {
            return Result.responseError(EntityError.ILLEGAL_ARGUMENT_ERROR);
        }

        ProcessDO process = new ProcessDO();
        process.setProcessId(processId);
        process.setValidState(VALID_STATE);
        process = processMapper.selectOne(new QueryWrapper<>(process));

        if (process == null) {
            return Result.responseError(EntityError.DATA_NOT_FOUND_ERROR);
        }
        ProcessDTO processDTO = BeanUtils.switchToDTO(process, ProcessDTO.class);

        return Result.responseSuccess(processDTO);
    }

    @Cacheable(value = "processDetail", key = "#p0", unless = "#result == null || #result.entityError == null || #result.entityError.code != 200")
    @Override
    public Result<ProcessDTO> getProcessByProcessKey(String processKey) {

        if (StringUtils.isEmpty(processKey)) {
            return Result.responseError(EntityError.ILLEGAL_ARGUMENT_ERROR);
        }
        ProcessDO process = new ProcessDO();
        process.setProcessKey(processKey);
        process.setValidState(VALID_STATE);
        process = processMapper.selectOne(new QueryWrapper<>(process));

        if (process == null) {
            return Result.responseError(EntityError.DATA_NOT_FOUND_ERROR);
        }
        ProcessDTO processDTO = BeanUtils.switchToDTO(process, ProcessDTO.class);

        return Result.responseSuccess(processDTO);
    }

    @Cacheable(value = "processWithDetail", key = "#p0", unless = "#result == null || #result.entityError == null || #result.entityError.code != 200")
    @Override
    public Result<ProcessInfoDTO> getProcessWithDetailByProcessKey(String processKey) {

        Result<ProcessDTO> result = getProcessByProcessKey(processKey);
        if (result.getEntityError().getCode() != EntityError.SUCCESS.getCode()) {
            return Result.responseError(result.getEntityError());
        }
        /* 加上一个 流程详细信息 */
        ProcessInfoDTO processInfoDTO = BeanUtils.switchToDTO(result.getData(), ProcessInfoDTO.class);
        Result<ProcessDetailDTO> result2 = processDetailService.getProcessDetailByProcessId(processInfoDTO.getProcessId());
        if (result2.getEntityError().getCode() != EntityError.SUCCESS.getCode()) {
            return Result.responseError(result2.getEntityError());
        }
        org.springframework.beans.BeanUtils.copyProperties(result2.getData(), processInfoDTO);

        return Result.responseSuccess(processInfoDTO);
    }

    @Override
    public Result<Integer> insertProcess(ProcessReqDTO processReqDTO) {

        if (processReqDTO == null
                || StringUtils.isEmpty(processReqDTO.getTenantId())
                || StringUtils.isEmpty(processReqDTO.getProcessKey())) {
            return Result.responseError(EntityError.ILLEGAL_ARGUMENT_ERROR);
        }
        // 默认 key 格式   租户 +"_" + key  , 不能包含 :  以字母或下划线 (_) 字符开头，后接 XML 规范中允许的任意字母、数字、重音字符、变音符号、句点 (.)、连字符 (-) 和下划线 (_) 的组合。
        if (!processReqDTO.getProcessKey().startsWith((processReqDTO.getTenantId()))) {
            processReqDTO.setProcessKey(processReqDTO.getTenantId() + "-" + "process" + "-" + processReqDTO.getProcessKey());
        }
        ProcessDO process = BeanUtils.switchToDO(processReqDTO, ProcessDO.class);
        Integer num = processMapper.insert(process);

        // 默认添加一个 流程详细信息
        ProcessDetailReqDTO processDetailReqDTO = BeanUtils.switchToDTO(process, ProcessDetailReqDTO.class);
        Result<Integer> result = processDetailService.insertProcessDetail(processDetailReqDTO);
        if (result.getEntityError().getCode() != EntityError.SUCCESS.getCode()) {
            return Result.responseError(result.getEntityError());
        }

        process.setProcessDetailId(result.getData().longValue());
        processMapper.updateById(process);
        return Result.responseSuccess(num);
    }
    @Caching(
            put = {
                    @CachePut(value = "processDetail", key = "#p0", unless = "#result == null || #result.entityError == null || #result.entityError.code != 200"),
                    @CachePut(value = "processDetail", key = "#p0", unless = "#result == null || #result.entityError == null || #result.entityError.code != 200"),
                    @CachePut(value = "processWithDetail", key = "#p0", unless = "#result == null || #result.entityError == null || #result.entityError.code != 200")
            },
            evict = {
                    @CacheEvict(value = "processDetail", key = "#p0.processKey")
            }
    )

    @Override
    public Result<Integer> updateProcessByProcessKey(ProcessReqDTO processReqDTO) {

        if (processReqDTO == null
                || StringUtils.isEmpty(processReqDTO.getProcessKey())) {
            return Result.responseError(EntityError.ILLEGAL_ARGUMENT_ERROR);
        }

        ProcessDO process = BeanUtils.switchToDO(processReqDTO, ProcessDO.class);
        Integer num = processMapper.updateProcessByProcessKey(process);
        return Result.responseSuccess(num);
    }

    @Override
    public Result<DynamicFormDataDTO> getInitStartFormData(String processKey) {

        BestBpmAsset.isAssetEmpty(processKey);

        Result<ProcessInfoDTO> result = getProcessWithDetailByProcessKey(processKey);
        if (result.getEntityError().getCode() != EntityError.SUCCESS.getCode()) {
            return Result.responseError(result.getEntityError());
        }
        ProcessInfoDTO processInfoDTO = result.getData();
        if (StringUtils.isEmpty(processInfoDTO.getDefinitionId())) {
            return Result.responseError(EntityError.UNPUBLISH_PROCESS_START_ERROR);
        }
        // 根据 流程定义 获取 发起类型为 start 的 用户任务节点，从这里获取发起表单
        NodeInfoReqDTO nodeInfoReqDTO = new NodeInfoReqDTO();
        nodeInfoReqDTO.setDefinitionId(processInfoDTO.getDefinitionId());
        nodeInfoReqDTO.setTaskType(BpmConstant.TASK_TYPE_START);
        nodeInfoReqDTO.setNodeType(BpmConstant.NODE_TYPE_USER_TASK);
        nodeInfoReqDTO.setValidState(VALID_STATE);
        Result<List<NodeInfoDTO>> result1 = nodeService.getNodeInfoListByCondition(nodeInfoReqDTO);
        if (result1.getEntityError().getCode() != EntityError.SUCCESS.getCode()) {
            return Result.responseError(result1.getEntityError());
        }

        NodeInfoDTO nodeInfoDTO = result1.getData().get(0);
        DynamicFormDataDTO dynamicFormDataDTO = new DynamicFormDataDTO();
        Map<String, List<TreeItemDTO>> dynamicDataMap = new HashMap<>();

        if (StringUtils.isEmpty(nodeInfoDTO.getFormKey())) {
            dynamicFormDataDTO.setDynamicDataMap(dynamicDataMap);
            return Result.responseSuccess(dynamicFormDataDTO);
        }

        Result<DynamicFormDataDTO> result2 = formService.getInitForm(nodeInfoDTO.getFormKey());
        if (result2.getEntityError().getCode() != EntityError.SUCCESS.getCode()) {
            return Result.responseError(result2.getEntityError());
        }
        dynamicFormDataDTO = result2.getData();
        /* 设置 formKey */
        dynamicFormDataDTO.setFormKey(nodeInfoDTO.getFormKey());
        dynamicFormDataDTO.setProcessId(processInfoDTO.getProcessId());
        dynamicFormDataDTO.setProcessKey(processInfoDTO.getProcessKey());
        dynamicFormDataDTO.setProcessName(processInfoDTO.getProcessName());
        return Result.responseSuccess(dynamicFormDataDTO);
    }

    @Override
    public Result<List<ItemDTO>> getProcessDict(String tenantId) {

        ProcessDO process = new ProcessDO();
        process.setTenantId(tenantId);
        process.setValidState(VALID_STATE);
        List<ProcessInfoDTO> processDTOList = processMapper.getListByCondition(process);

        List<ItemDTO> list = new ArrayList<>();
        ItemDTO itemDTO = null;
        if(processDTOList != null){
            for(ProcessInfoDTO processInfoDTO:processDTOList){
                itemDTO = new ItemDTO();
                itemDTO.setLabel(processInfoDTO.getProcessName());
                itemDTO.setValue(processInfoDTO.getProcessId());
                list.add(itemDTO);
            }
        }
        return Result.responseSuccess(list);
    }

}
